<template>
  <div
    :class="['client-chat', { 'mobile-chat': isMobile }]"
    v-scrollto.top.10="onScroll"
    :style="{
      height: webIMSource === 'workbench' ? `${chatBoxHeight}px` : '100%',
    }"
  >
    <v-popper ref="first" clazz="pop-demo" :use-arrow="true">
      <div class="pop-demo-list" v-for="pop in popperList" :key="pop.id">
        <!-- <a :href="pop.url" target="_blank">{{pop.id + '.' + pop.name }} ></a> -->
        <v-button
          type="link"
          kind="primary"
          v-blank
          @click="openSearchUrl(pop, pop.id)"
        >
          <!-- {{pop.id + '.' + pop.name }} -->
          {{ pop.id }}.{{
            isMobile
              ? formattedEllipsis(pop.name, 15)
              : formattedEllipsis(pop.name)
          }}
          <v-icon remote name="arrow_right_line" size="12" valign="-1"></v-icon>
        </v-button>
      </div>
    </v-popper>
    <!-- <div id="renderChat"></div> -->
    <!-- 上滑加载 -->
    <div :class="['qa-history', { isMobile: isMobile }]">
      <v-spinner status="default" v-if="historyLoading"
        >会话记录加载中</v-spinner
      >
    </div>
    <audio id="audioplayer" controls></audio>
    <div class="qa-null" v-if="showGreetingBanner">
      <div class="title-robot-avatar">
        <img v-if="!!robotAvatar" :src="robotAvatar" />
      </div>
      <div class="title-desc">{{ robotName }}</div>
      <div v-if="greetingDesc" class="greet-desc">{{ greetingDesc }}</div>
      <RecommendQuestionList type="greeting" @itemClick="sendQuestion" />
    </div>

    <template v-else>
      <!-- <div class="qa-item" v-for="(item, index) in msgList" :key="index"> -->
      <div
        :class="['qa-item', { 'qa-item-last': index === msgList.length - 1 }]"
        v-for="(item, index) in msgList"
        :key="index"
        :data-id="item.record_id"
      >
        <!-- 时间戳 -->
        <div
          class="timestamp"
          v-if="
            index === 0 ||
            (index !== 0 &&
              item.timestamp &&
              Number(item.timestamp) - Number(msgList[index - 1].timestamp) >
                timestampGap)
          "
        >
          {{
            moment(
              new Date(
                String(item.timestamp).length === 10
                  ? item.timestamp * 1000
                  : Number(item.timestamp)
              )
            ).format("MM-DD HH:mm")
          }}
        </div>

        <!-- 消息上下文关联清空标识 -->
        <!-- <div class="break-releation-class" v-if="item.isBreakReleationTop">-------以下为新话题------</div> -->
        <div class="break-releation-class" v-if="item.isBreakReleationTop">
          <DividerDemo
            :content="'以下是新对话'"
            :max-width="'480px'"
          ></DividerDemo>
        </div>
        <!-- 问题 文件 -->
        <template v-if="item.file_infos && item.file_infos.length > 0">
          <div
            class="question-item question-doc-item"
            v-for="(doc, index) in item.file_infos"
            :key="index"
            @click="previewDoc(doc)"
          >
            <v-icon
              name="v-error"
              class="qs-error"
              size="16"
              v-if="item.failed || item.is_evil"
            ></v-icon>
            <v-spinner
              status="default"
              class="qs-loading"
              v-else-if="item.is_loading"
            ></v-spinner>
            <div class="doc-icon-cont">
              <v-icon
                :name="getFileIcon(doc.file_type)"
                remote
                class="doc-icon"
              >
              </v-icon>
            </div>
            <div class="doc-txt-cont">
              <div
                class="filename"
                :title="`${doc.file_name}.${doc.file_type}`"
              >
                {{ fileString(doc.file_name, 6, doc.file_type) }}
              </div>
              <div class="doc-txt-info">
                {{ `${formateSize(doc.file_size)} · 已发送` }}
              </div>
            </div>
          </div>
        </template>
        <div
          class="question-item"
          v-if="(item.is_from_self || !item.from_name) && item.content"
        >
          <v-icon
            name="v-error"
            class="qs-error"
            size="16"
            v-if="item.failed || item.is_evil"
          ></v-icon>
          <v-spinner
            status="default"
            class="qs-loading"
            v-else-if="item.is_loading"
          ></v-spinner>
          <MsgContent
            :fullFiles="getFilesList(item)"
            :showDownloadCard="true"
            :linkify="true"
            @imageClick="imageClick"
            @littleTagClick="littleTagClick"
            :showTags="true"
            :recordId="item.record_id"
            :isReplaceLinks="true"
            :loadingMessage="item.loading_message"
            :content="item.content"
            :isFinal="item.is_final"
            :isMdExpand="true"
            :clazzMd="`question-text ${
              webIMSource === 'client' && isMobile ? 'question-text-mobile' : ''
            }`"
            :styleObj="{
              'max-width':
                webIMSource === 'client' && isMobile ? '352px' : '100%',
            }"
            :codeHeaderRender="codeHeaderRender"
          />
        </div>

        <!--【坐席端】客户接入标识 -->
        <div class="chat-status-item" v-if="item.enter">
          <div class="status-icon green"></div>
          <div class="status-text">客户已接入</div>
        </div>

        <!--【坐席端】客服发送敏感词提示 -->
        <div class="chat-status-item" v-if="item.is_evil && item.is_from_self">
          <div class="status-icon red"></div>
          <div class="status-text">消息内容包含敏感信息，发送失败</div>
        </div>
        
        <template>
          <!-- 答案 -->
          <template v-if="apptype === APP_TYPE.QA">
            <div
              class="answer-item"
              v-if="
                !item.is_from_self &&
                item.msg_type !== 'summary' &&
                item.msg_type !== 'abstract'
              "
            >
              <!-- 头像 -->
              <div
                class="answer-avatar"
                v-if="!item.is_from_self && item.from_name"
              >
                <img class="robot-avatar" :src="item.from_avatar" />
              </div>

              <!-- 答案信息 -->
              <div class="answer-info-container">
                <template
                  v-if="
                    !item.content &&
                    item.tokens_msg &&
                    item.tokens_msg.status_summary === TOKENS_STATUS.error
                  "
                >
                  <div class="answer-info">
                    <p class="answer-error">
                      {{ item.tokens_msg.status_summary_title }}
                    </p>
                  </div>
                </template>
                <template
                  v-else-if="
                    item.work_flow &&
                    item.work_flow.outputsVal &&
                    item.work_flow.outputsVal.length > 1
                  "
                >
                  <div
                    v-for="(ee, idnx) in item.work_flow.outputsVal"
                    :key="idnx"
                    @click="handleClickAnswerInfo(item)"
                    :ref="item.record_id"
                  >
                    <div
                      :class="[
                        'answer-info',
                        { 'is-selected': item.isSelected },
                      ]"
                      :style="idnx !== 0 ? 'margin-top: 8px' : ''"
                    >
                      <!-- 思考过程 ThoughtCollapse-->
                      <ThoughtCollapse
                        :msgIndex="index"
                        @openWorkspace="onOpenWorkspace"
                        v-if="item.newAgentThoughts[idnx]"
                        :ref="'thought' + item.record_id"
                        :agentThought="item.newAgentThoughts[idnx]"
                        @openBottomDrawer="openBottomDrawer"
                      ></ThoughtCollapse>

                      <!-- 非转人工情况下, 展示临时[正在思考中]消息 -->
                      <MsgLoading
                        :loadingIcon="loadingIcon"
                        :loadingMessage="item.loading_message"
                        :loadingText="item.text"
                        :transferStatus="transferStatus"
                      />
                      <!-- Markdown渲染 -->
                      <MsgContent
                        :fullFiles="getFilesList(item)"
                        :showDownloadCard="true"
                        :linkify="true"
                        @imageClick="imageClick"
                        @littleTagClick="littleTagClick"
                        :showTags="true"
                        :recordId="item.record_id + '-idx-' + idnx"
                        :isReplaceLinks="true"
                        :loadingMessage="item.loading_message"
                        :content="ee.value"
                        :isFinal="item.is_final"
                        :isMdExpand="ee.isMdExpand"
                        :clazzTable="item.is_final ? 'table-style' : 'table'"
                        clazzMd="answer-md"
                        :styleObj="{
                          'max-width':
                            webIMSource === 'client' && isMobile
                              ? '310px'
                              : '100%',
                        }"
                        :anchorAttributes="{ target: '_blank' }"
                        :codeHeaderRender="codeHeaderRender"
                      />
                      <MsgChart
                        v-if="item.extra_info && item.extra_info.e_charts_info"
                        :chartContainerId="item.record_id"
                        :chartDataArr="
                          changeLegend(item.extra_info.e_charts_info)
                        "
                      />
                      <div
                        v-if="
                          item.is_final &&
                          item.option_cards &&
                          item.option_cards.length > 0 &&
                          item.work_flow.outputsVal.length - 1 === idnx
                        "
                        class="answer-options"
                        v-height-watch="onHeightChange"
                        ref="watchedElement"
                        :style="{
                          'max-height': item.isMdExpand ? 'initial' : '277px',
                        }"
                      >
                        <div
                          v-for="(card, cIndex) in item.option_cards"
                          :class="[
                            'option-card',
                            item.option_selected || index !== msgList.length - 1
                              ? 'disabled'
                              : '',
                            item.option_card_index === cIndex
                              ? 'is-active'
                              : '',
                          ]"
                          :key="cIndex"
                          @click="clickCard(card, index)"
                        >
                          {{ card }}
                        </div>
                      </div>
                      <div
                        class="answer-expand"
                        @click="onExpandMd(ee)"
                        v-if="
                          (item.is_final &&
                            ee.value &&
                            ee.value.length > 3000) ||
                          optionExpend
                        "
                      >
                        <!-- <v-icon :name="(isMdExpand || !item.is_final) ? 'arrow_up_small_line' : 'arrow_down_small_line'" remote /> -->
                        <v-icon
                          :name="
                            ee.isMdExpand
                              ? 'arrow_up_small_line'
                              : 'arrow_down_small_line'
                          "
                          remote
                        />
                      </div>
                    </div>
                  </div>
                </template>
                <template v-else>
                  <div
                    v-if="!item.can_rating && !disableInput[currentVisitorId]"
                    v-tip.hover.delay200="
                      webIMSource === 'workbench' && 'autoButton' + index
                    "
                    class="answer-info"
                    :ref="item.record_id"
                    @mouseover="calHoverGap(index)"
                    @mouseleave="calHoverGap"
                  >
                    <!-- 功能气泡 -->
                    <v-tip
                      clazz="chat-pop"
                      :gap="hoverGap"
                      :ref="`autoButton${index}`"
                      placement="right-start"
                      v-if="showAutoSearch && item.showPop"
                    >
                      <div class="answer-func-icon">
                        <div
                          class="right-icon-div"
                          title="智能辅助查询"
                          @mouseover="showIconHover"
                          @mouseleave="hideIconHover"
                          @click="autoSearchUserAnswer(index)"
                        >
                          <div v-if="!iconHover">
                            <v-icon
                              name="menu_smart_assistance"
                              remote
                              size="20"
                              class="right-icon"
                            />
                          </div>
                          <div v-else-if="iconHover && !isActive">
                            <img
                              :src="`/SmartWebim/magichover.gif?t=${iconRender}`"
                              class="right-icon magic-hover"
                            />
                          </div>
                          <div v-else-if="isActive">
                            <v-icon
                              name="menu_smart_assistance"
                              remote
                              size="20"
                              class="right-icon active-icon"
                            />
                          </div>
                        </div>
                        <div class="icon-divider"></div>
                        <div class="right-icon-div" title="复制">
                          <v-icon
                            class="right-icon icon-fill"
                            name="basic_copy_fill"
                            remote
                            size="20"
                          />
                        </div>
                        <div class="icon-divider"></div>
                        <div class="right-icon-div" title="更多">
                          <v-icon
                            class="right-icon icon-fill"
                            name="basic_more3_line"
                            remote
                            size="20"
                          />
                        </div>
                      </div>
                    </v-tip>
                    <ThoughtCollapse
                      :workspaceVisible="workspaceVisible"
                      :msgIndex="index"
                      @openWorkspace="onOpenWorkspace"
                      :ref="'thought' + item.record_id"
                      :agentThought="item.agent_thought"
                      @openBottomDrawer="openBottomDrawer"
                    ></ThoughtCollapse>
                    <!-- <ThoughtCollapse style="margin-top: 12px;" :is-open.sync="openThoughtClps[item.record_id]" :agentThought="item.agent_thought"></ThoughtCollapse> -->

                    <!-- 工作流结束，并且没有内容回复 -->
                    <div
                      v-if="item.reply_method === 17 && !item.content"
                      class="answer-md ept-content"
                    >
                      <p>无消息输出</p>
                    </div>
                    <!-- 非转人工情况下, 展示临时[正在思考中]消息 -->
                    <MsgLoading
                      :loadingIcon="loadingIcon"
                      :loadingMessage="item.loading_message"
                      :loadingText="item.text"
                      :transferStatus="transferStatus"
                    />
                    <!-- Markdown渲染 -->
                    <MsgContent
                      v-if="!item.is_from_self && item.from_name"
                      :fullFiles="getFilesList(item)"
                      :showDownloadCard="true"
                      :linkify="true"
                      @imageClick="imageClick"
                      @littleTagClick="littleTagClick"
                      :showTags="true"
                      :recordId="item.record_id"
                      :isReplaceLinks="true"
                      :loadingMessage="item.loading_message"
                      :content="item.content"
                      :isFinal="item.is_final"
                      :isMdExpand="item.isMdExpand"
                      :clazzTable="item.is_final ? 'table-style' : 'table'"
                      clazzMd="answer-md"
                      :styleObj="{
                        'max-width':
                          webIMSource === 'client' && isMobile
                            ? '310px'
                            : '100%',
                      }"
                      :codeHeaderRender="codeHeaderRender"
                      :anchorAttributes="{
                        target: '_blank',
                        rel: 'noreferrer',
                      }"
                    />
                    <MsgChart
                      v-if="item.extra_info && item.extra_info.e_charts_info"
                      :chartContainerId="item.record_id"
                      :chartDataArr="
                        changeLegend(item.extra_info.e_charts_info)
                      "
                    />
                    <div
                      v-if="
                        item.is_final &&
                        item.option_cards &&
                        item.option_cards.length > 0
                      "
                      class="answer-options"
                      v-height-watch="onHeightChange"
                      ref="watchedElement"
                      :style="{
                        'max-height': item.isMdExpand ? 'initial' : '277px',
                      }"
                    >
                      <div
                        v-for="(card, cIndex) in item.option_cards"
                        :class="[
                          'option-card',
                          item.option_selected || index !== msgList.length - 1
                            ? 'disabled'
                            : '',
                          item.option_card_index === cIndex ? 'is-active' : '',
                        ]"
                        :key="cIndex"
                        @click="clickCard(card, index)"
                      >
                        {{ card }}
                      </div>
                    </div>

                    <div
                      class="answer-expand"
                      @click="onExpandMd(item)"
                      v-if="
                        (item.is_final &&
                          item.content &&
                          item.content.length > 3000) ||
                        optionExpend
                      "
                    >
                      <!-- <v-icon :name="(isMdExpand || !item.is_final) ? 'arrow_up_small_line' : 'arrow_down_small_line'" remote /> -->
                      <v-icon
                        :name="
                          item.isMdExpand
                            ? 'arrow_up_small_line'
                            : 'arrow_down_small_line'
                        "
                        remote
                      />
                    </div>

                    <div class="answer-clarify" v-if="item.reply_method === 15">
                      <div
                        :key="ctIndex"
                        v-for="(question, ctIndex) in item.clarifyResults"
                        @click="handleClarifyQuestion($event, question)"
                      >
                        {{ question }}
                      </div>
                    </div>

                    <!-- 对话测试 来源 -->
                    <div
                      class="answer-source ccc"
                      v-if="
                        item.references &&
                        item.references.length > 0 &&
                        item.is_final
                      "
                    >
                      <div>参考来源：</div>
                      <div
                        v-for="(sItem, sIndex) in item.references"
                        :key="sIndex"
                      >
                        <v-button
                          :href="sItem.url"
                          v-if="sItem.type === 2"
                          type="link"
                          kind="primary"
                          @click="
                            openSourceDrawer(item.references, sIndex, $event)
                          "
                        >
                          <v-icon remote name="basic_papers_line"></v-icon>
                          {{ sItem.name }}
                          <v-icon
                            remote
                            name="arrow_right_line"
                            size="12"
                            valign="-1"
                          ></v-icon>
                        </v-button>
                        <v-button
                          v-else-if="sItem.type === 4"
                          type="link"
                          kind="primary"
                          v-blank
                          @click="openSearchUrl(sItem, sIndex)"
                        >
                          <v-icon remote name="basic_url_line"></v-icon>
                          {{ sItem.id }}.{{
                            isMobile
                              ? formattedEllipsis(sItem.name, 15)
                              : formattedEllipsis(sItem.name)
                          }}
                          <v-icon
                            remote
                            name="arrow_right_line"
                            size="12"
                            valign="-1"
                          ></v-icon>
                        </v-button>
                        <v-button
                          v-else
                          type="link"
                          :href="sItem.url"
                          kind="primary"
                          @click="
                            openSourceDrawer(item.references, sIndex, $event)
                          "
                        >
                          <v-icon remote name="basic_q_a_line"></v-icon>
                          {{ sItem.name }}
                          <v-icon
                            remote
                            name="arrow_right_line"
                            size="12"
                            valign="-1"
                          ></v-icon>
                        </v-button>
                      </div>
                    </div>
                  </div>
                  <div v-else class="answer-info" :ref="item.record_id">
                    <ThoughtCollapse
                      :workspaceVisible="workspaceVisible"
                      :msgIndex="index"
                      @openWorkspace="onOpenWorkspace"
                      :ref="'thought' + item.record_id"
                      :agentThought="item.agent_thought"
                      @openBottomDrawer="openBottomDrawer"
                    ></ThoughtCollapse>

                    <!-- 工作流结束，并且没有内容回复 -->
                    <div
                      v-if="item.reply_method === 17 && !item.content"
                      class="answer-md ept-content"
                    >
                      <p>无消息输出</p>
                    </div>
                    <!-- 非转人工情况下, 展示临时[正在思考中]消息 -->
                    <MsgLoading
                      :loadingIcon="loadingIcon"
                      :loadingMessage="item.loading_message"
                      :loadingText="item.text"
                      :transferStatus="transferStatus"
                    />
                    <!-- Markdown渲染 -->
                    <MsgContent
                      :fullFiles="getFilesList(item)"
                      :showDownloadCard="true"
                      :linkify="true"
                      @imageClick="imageClick"
                      @littleTagClick="littleTagClick"
                      :showTags="true"
                      :recordId="item.record_id"
                      :isReplaceLinks="true"
                      :loadingMessage="item.loading_message"
                      :content="item.content"
                      :isFinal="item.is_final"
                      :isMdExpand="item.isMdExpand"
                      :clazzTable="item.is_final ? 'table-style' : 'table'"
                      clazzMd="answer-md"
                      :styleObj="{
                        'max-width':
                          webIMSource === 'client' && isMobile
                            ? '310px'
                            : '100%',
                      }"
                      :anchorAttributes="{
                        target: '_blank',
                        rel: 'noreferrer',
                      }"
                      :codeHeaderRender="codeHeaderRender"
                    />
                    <MsgChart
                      v-if="item.extra_info && item.extra_info.e_charts_info"
                      :chartContainerId="item.record_id"
                      :chartDataArr="
                        changeLegend(item.extra_info.e_charts_info)
                      "
                    />

                    <div
                      v-if="
                        item.is_final &&
                        item.option_cards &&
                        item.option_cards.length > 0
                      "
                      class="answer-options"
                      v-height-watch="onHeightChange"
                      ref="watchedElement"
                      :style="{
                        'max-height': item.isMdExpand ? 'initial' : '277px',
                      }"
                    >
                      <div
                        v-for="(card, cIndex) in item.option_cards"
                        :class="[
                          'option-card',
                          item.option_selected || index !== msgList.length - 1
                            ? 'disabled'
                            : '',
                          item.option_card_index === cIndex ? 'is-active' : '',
                        ]"
                        :key="cIndex"
                        @click="clickCard(card, index)"
                      >
                        {{ card }}
                      </div>
                    </div>

                    <div
                      class="answer-expand"
                      @click="onExpandMd(item)"
                      v-if="
                        item.is_final &&
                        item.content &&
                        item.content.length > 3000
                      "
                    >
                      <v-icon
                        :name="
                          item.isMdExpand
                            ? 'arrow_up_small_line'
                            : 'arrow_down_small_line'
                        "
                        remote
                      />
                    </div>

                    <div class="answer-clarify" v-if="item.reply_method === 15">
                      <div
                        :key="ctIndex"
                        v-for="(question, ctIndex) in item.clarifyResults"
                        @click="handleClarifyQuestion($event, question)"
                      >
                        {{ question }}
                      </div>
                    </div>

                    <!-- web体验链接 来源 -->
                    <div
                      class="answer-source eee"
                      v-if="
                        item.references &&
                        item.references.length > 0 &&
                        item.references.filter(
                          (i) => i.type === 2 || i.type === 4
                        ).length > 0 &&
                        item.is_final
                      "
                    >
                      <div
                        v-if="
                          item.references.filter(
                            (i) => i.type === 2 || i.type === 4
                          ).length > 0
                        "
                      >
                        参考来源：
                      </div>
                      <div
                        v-for="(sItem, sIndex) in item.references"
                        :key="sIndex"
                      >
                        <v-button
                          :href="sItem.url"
                          v-if="sItem.type === 2"
                          type="link"
                          kind="primary"
                          @click="
                            openSourceDrawer(item.references, sIndex, $event)
                          "
                        >
                          <v-icon remote name="basic_papers_line"></v-icon>
                          {{ sItem.name }}
                          <v-icon
                            remote
                            name="arrow_right_line"
                            size="12"
                            valign="-1"
                          ></v-icon>
                        </v-button>
                        <v-button
                          v-if="sItem.type === 4"
                          type="link"
                          kind="primary"
                          v-blank
                          @click="openSearchUrl(sItem, sIndex)"
                        >
                          <v-icon remote name="basic_url_line"></v-icon>
                          {{ sItem.id }}.{{
                            isMobile
                              ? formattedEllipsis(sItem.name, 15)
                              : formattedEllipsis(sItem.name)
                          }}
                          <v-icon
                            remote
                            name="arrow_right_line"
                            size="12"
                            valign="-1"
                          ></v-icon>
                        </v-button>
                      </div>
                    </div>
                  </div>
                </template>
                <!-- ReplyMethod 为 9（任务流程回复）， 10（任务流程答案回复）， 16 和 17（工作流回复 新工作流）  17标识结束节点-  18 agent类型特殊处理 -->
                <template v-if="webIMSource === 'manage' && item.is_final">
                </template>
                <template v-else>
                  <Feedback
                    style="margin-top: 8px"
                    class="answer-feedback"
                    v-if="
                      (webIMSource === 'client' || webIMSource === 'manage') &&
                      item.record_id &&
                      (item.is_final || item.is_connection_error) &&
                      item.reply_method !== 15
                    "
                    :msgData="item"
                    :msgList="msgList"
                  />
                </template>
                <!-- 推荐问 -->
                <Recommend
                  class="tokens-board-class"
                  v-if="item.recommended && index === msgList.length - 1"
                  :recommendData="item.recommended"
                  @sendRecommendQa="sendQuestion"
                ></Recommend>
              </div>
            </div>
          </template>
          <!-- 这里展示非qa问答的情况 -->
          <template v-else>
            <div
              class="answer-item"
              v-if="
                !item.is_from_self &&
                item.msg_type !== 'summary' &&
                item.msg_type !== 'abstract'
              "
            >
              <!-- 头像 -->
              <div class="answer-avatar">
                <img class="robot-avatar" :src="item.from_avatar" />
              </div>

              <!-- 答案信息 -->
              <template
                v-if="
                  !item.content &&
                  item.tokens_msg &&
                  item.tokens_msg.status_summary === TOKENS_STATUS.error
                "
              >
                <div class="answer-info">
                  <p class="answer-error">
                    {{ item.tokens_msg.status_summary_title }}
                  </p>
                </div>
              </template>
              <template
                v-else-if="
                  item.work_flow &&
                  item.work_flow.outputsVal &&
                  item.work_flow.outputsVal.length > 1
                "
              >
                <div
                  v-for="(ee, idnx) in item.work_flow.outputsVal"
                  :key="idnx"
                  @click="handleClickAnswerInfo(item)"
                  :ref="item.record_id"
                >
                  <div
                    :class="['answer-info', { 'is-selected': item.isSelected }]"
                    :style="idnx !== 0 ? 'margin-top: 8px' : ''"
                  >
                    <!-- 思考过程 ThoughtCollapse-->
                    <!-- <ThoughtCollapse v-if="showMultiThought(idnx, item)" :agentThought="item.agent_thought"></ThoughtCollapse> -->
                    <ThoughtCollapse
                      :msgIndex="index"
                      @openWorkspace="onOpenWorkspace"
                      v-if="item.newAgentThoughts[idnx]"
                      :ref="'thought' + item.record_id"
                      :agentThought="item.newAgentThoughts[idnx]"
                      @openBottomDrawer="openBottomDrawer"
                    ></ThoughtCollapse>

                    <!-- 非转人工情况下, 展示临时[正在思考中]消息 -->
                    <MsgLoading
                      :loadingIcon="loadingIcon"
                      :loadingMessage="item.loading_message"
                      :loadingText="item.text"
                      :transferStatus="transferStatus"
                    />
                    <!-- Markdown渲染 -->
                    <MsgContent
                      :fullFiles="getFilesList(item)"
                      :showDownloadCard="true"
                      :linkify="true"
                      @imageClick="imageClick"
                      @littleTagClick="littleTagClick"
                      :showTags="true"
                      :recordId="item.record_id + '-idx-' + idnx"
                      :isReplaceLinks="true"
                      :loadingMessage="item.loading_message"
                      :content="ee.value"
                      :isFinal="item.is_final"
                      :isMdExpand="ee.isMdExpand"
                      :clazzTable="item.is_final ? 'table-style' : 'table'"
                      clazzMd="answer-md"
                      :styleObj="{
                        'max-width':
                          webIMSource === 'client' && isMobile
                            ? '310px'
                            : '680px',
                      }"
                      :anchorAttributes="{ target: '_blank' }"
                      :codeHeaderRender="codeHeaderRender"
                    />
                    <MsgChart
                      v-if="item.extra_info && item.extra_info.e_charts_info"
                      :chartContainerId="item.record_id"
                      :chartDataArr="
                        changeLegend(item.extra_info.e_charts_info)
                      "
                    />
                    <div
                      class="answer-expand"
                      @click="onExpandMd(ee)"
                      v-if="
                        (item.is_final && ee.value && ee.value.length > 3000) ||
                        optionExpend
                      "
                    >
                      <!-- <v-icon :name="(isMdExpand || !item.is_final) ? 'arrow_up_small_line' : 'arrow_down_small_line'" remote /> -->
                      <v-icon
                        :name="
                          ee.isMdExpand
                            ? 'arrow_up_small_line'
                            : 'arrow_down_small_line'
                        "
                        remote
                      />
                    </div>
                  </div>
                </div>
              </template>
              <template v-else>
                <div
                  class="summary-info"
                  :ref="item.record_id"
                  v-if="apptype === APP_TYPE.SUMMARY"
                >
                  <!-- 工作流结束，并且没有内容回复 -->
                  <div
                    v-if="item.reply_method === 17 && !item.content"
                    class="answer-md ept-content"
                  >
                    <p>无消息输出</p>
                  </div>
                  <!-- 非转人工情况下, 展示临时[正在思考中]消息 -->
                  <MsgLoading
                    :loadingIcon="loadingIcon"
                    :loadingMessage="item.loading_message"
                    :loadingText="item.text"
                    :transferStatus="transferStatus"
                  />
                  <!-- Markdown渲染 -->
                  <MsgContent
                    :fullFiles="getFilesList(item)"
                    :showDownloadCard="true"
                    :linkify="true"
                    @imageClick="imageClick"
                    @littleTagClick="littleTagClick"
                    :showTags="true"
                    :recordId="item.record_id"
                    :isReplaceLinks="true"
                    :loadingMessage="item.loading_message"
                    :content="item.content"
                    :isFinal="item.is_final"
                    :isMdExpand="item.isMdExpand"
                    :clazzTable="item.is_final ? 'table-style' : 'table'"
                    clazzMd="answer-md"
                    :styleObj="{
                      'max-width':
                        webIMSource === 'client' && isMobile
                          ? '310px'
                          : '680px',
                    }"
                    :anchorAttributes="{ target: '_blank', rel: 'noreferrer' }"
                    :codeHeaderRender="codeHeaderRender"
                  />
                  <MsgChart
                    v-if="item.extra_info && item.extra_info.e_charts_info"
                    :chartContainerId="item.record_id"
                    :chartDataArr="changeLegend(item.extra_info.e_charts_info)"
                  />
                  <div
                    class="answer-expand"
                    @click="onExpandMd(item)"
                    v-if="
                      item.is_final &&
                      item.content &&
                      item.content.length > 3000
                    "
                  >
                    <v-icon
                      :name="
                        item.isMdExpand
                          ? 'arrow_up_small_line'
                          : 'arrow_down_small_line'
                      "
                      remote
                    />
                  </div>
                </div>
                <div class="classify-info" v-else>
                  <div
                    class="tag-loading"
                    v-if="item.loading_message && !transferStatus"
                  >
                    {{ !item.text ? "正在思考中" : item.text }}
                  </div>
                  <div class="classify-container" v-else-if="!item.tags">
                    <span v-if="item.content">{{ item.content }}</span
                    ><span v-else class="classify-tag">null</span>
                  </div>
                  <template v-else>
                    <div
                      class="classify-container"
                      v-for="(item, index) in item.tags"
                      :key="index"
                    >
                      <div class="classify-label">{{ item.name }}：</div>
                      <!-- <div class="classify-content">
                                <span class="classify-tag" v-for="(ele, idx) in item.value_range" :key="idx"> {{ ele || 'null' }}</span>
                            </div> -->

                      <div class="classify-content" v-if="item.value_range">
                        <span
                          class="classify-tag"
                          v-for="(ele, idx) in item.value_range"
                          :key="idx"
                        >
                          {{ ele || "null" }}</span
                        >
                      </div>
                      <div class="classify-content" v-else>
                        <span class="classify-tag">null</span>
                      </div>
                    </div>
                  </template>
                </div>
              </template>
            </div>
            <div
              class="answer-item"
              v-if="
                !item.is_from_self &&
                item.msg_type !== 'summary' &&
                item.msg_type !== 'abstract'
              "
            >
              <!-- ReplyMethod 为 9（任务流程回复）， 10（任务流程答案回复）， 16 17（工作流回复 新工作流）  - 18 agent-->
              <template v-if="webIMSource === 'manage'">
              </template>
              <Recommend
                class="tokens-board-class"
                v-if="item.recommended && index === msgList.length - 1"
                :recommendData="item.recommended"
                @sendRecommendQa="sendQuestion"
              ></Recommend>
            </div>
          </template>
        </template>
        <!-- 消息上下文关联清空标识 -->
        <!-- <div class="break-releation-class" v-if="item.isBreakReleation">-------以下为新话题------</div> -->
        <div class="break-releation-class" v-if="item.isBreakReleation">
          <DividerDemo
            :content="'以下是新对话'"
            :max-width="'480px'"
          ></DividerDemo>
        </div>
        <div class="break-releation-class" v-if="item.stopVoice">
          <DividerDemo
            content="已经结束通话，对话已更新"
            :max-width="'480px'"
          ></DividerDemo>
        </div>

        <!--【用户端】会话转接到人工标识 -->
        <div class="transfer-item" v-if="item.transfer">
          <img class="transfer-avatar" :src="item.avatar" />
          <div class="transfer-text">腾讯企点人工客服为您服务</div>
          <div class="transfer-timestamp">
            {{
              item.timestamp
                ? moment(new Date(Number(item.timestamp) * 1000)).format(
                    "MM-DD HH:mm"
                  )
                : ""
            }}
          </div>
        </div>

        <!-- 【多端通用】会话结束标识 -->
        <div class="chat-status-item" v-if="item.quit">
          <div class="status-icon gray"></div>
          <div class="status-text">会话已结束</div>
        </div>
        <!--【用户端】会话转接到智能客服标识 -->
        <div v-if="item.transferRobot && !item.quit" class="transfer-info">
          <div class="chat-status-item">
            <div class="status-text">{{ robotName }}为您服务</div>
          </div>
        </div>
      </div>
    </template>
  </div>
</template>

<script>
import clone from "clone";
import elementResizeDetectorMaker from "element-resize-detector";
import Feedback from "./feedback.vue";
import Recommend from "./recommend.vue";
import ThoughtCollapse from "./thought-collapse.vue";
import DividerDemo from "./divider-demo.vue";
import Cookies from "js-cookie";
import {
  getQueryVariable,
  getUserCategory,
  getShareCode,
  scrollToBottom,
  debounce,
  debounceOne,
  replaceLinks,
  escapeSingleTilde,
  escapeMarkdownStars,
  getFileIcon,
  formateSize,
  fileString,
  formattedEllipsis,
  ttsThrottled,
} from "@/utils";
import {
  MESSAGE_TYPE,
  APP_TYPE,
  TOKENS_STATUS,
  SENTENCECHAR,
} from "@/constants";
import RecommendQuestionList from "@/pages/chat/components/recommend-question-list.vue";
import lodash, { get as _get } from "lodash";
import msgCodeHeader from "@/mixins/msg-code-header";
import msgHelper from "@/mixins/msg-helper.js";
import AiLoading from "@/assets/img/ai-loading.gif";
import { scrollto } from "@/utils/scrollTo";
const md = require("markdown-it")();

export default {
  name: "ClientChat",
  mixins: [msgCodeHeader, msgHelper],
  components: {
    RecommendQuestionList,
    Feedback,
    Recommend,
    ThoughtCollapse,
    DividerDemo,
  },
  props: {
    showAutoSearch: {
      type: Boolean,
      default: false,
    },
    autoPlay: {
      type: Boolean,
      default: false,
    },
    workspaceVisible: Boolean,
  },
  directives: {
    heightWatch: {
      bind(el, binding, vnode) {
        const onHeightChange = binding.value;
        const observer = new ResizeObserver((entries) => {
          for (const entry of entries) {
            const height = entry.contentRect.height;
            onHeightChange(height, vnode.context);
          }
        });
        observer.observe(el);
        el.__resizeObserver__ = observer;
      },
      unbind(el) {
        if (el.__resizeObserver__) {
          el.__resizeObserver__.disconnect();
          delete el.__resizeObserver__;
        }
      },
    },
    blank: {
      inserted(el) {
        if (el.tagName === "A") {
          el.setAttribute("target", "_blank");
        }
      },
    },
    scrollto,
  },
  computed: {
    showGreetingBanner() {
      if (this.msgList.length === 0) {
        return true;
      } else if (this.msgList.length === 1 && this.msgList[0].type === 6) {
        return true;
      } else {
        return false;
      }
    },
  },
  watch: {
    autoPlay: function (val) {
      if (!val && !this.lastRealTimeMsgEnd) {
        this.$eventHub.$emit("reading_audio", {
          reading: false,
        });
        this.lastRealTimeMsgEnd = true;
      }
    },
  },
  data() {
    const isExperience = !!getQueryVariable("experience");

    console.log("tempTxt", this.webIMSource);
    // const from = getIframeQueryVariable('from');
    return {
      lastRealTimeMsgEnd: true,
      aiConfig: {
        enable_digital_human: false,
        enable_voice_call: false,
        enable_voice_interact: false,
      },
      //   tempChart1,
      chartDataArr: [],
      // openThoughtClps: [],
      oldPopDemo: null,
      popperList: [],
      loadingIcon: AiLoading,
      TOKENS_STATUS,
      APP_TYPE,
      apptype: APP_TYPE.QA,
      transferStatus: false,
      isExperience,
      greetingDesc: "",
      iconHover: false,
      isActive: true,
      iconRender: Math.random(),
      hoverGap: 0,
      ishoverPop: false,
      loading: false,
      historyLoading: false,
      // isMdExpand: false,
      optionExpend: false,
      isFirstGetHistory: true, // 是否首次拉取历史对话，解决坐席端首次进入对话并拉取历史后，需要滚动到底部问题。用于区分非首次下拉滚动后的滚动条定位
      timestampGap: 5 * 60, // 两次问题间隔大于5min，则展示时间戳（接口侧返回的时间戳是秒级）
      msgList: [], // 对话消息列表
      botBizId: 0, // 机器人明文id
      robotName: "", // 机器人名称
      robotAvatar: "", // 机器人头像
      currentVisitorId: 0, // 坐席端-当前联系人id
      chatBoxHeight: document.body.clientHeight,
      disableInput: {},
      jsScrolling: false,
      userScrolling: false,
      connectionError: false,
      systemError: [
        "disconnect", // 连接异常中断
        "connectError", // 连接错误
        "SocketError", // 连接请求错误
        "reconnectError", // 重新连接错误
        "reconnectFailed", // 重新连接失败
      ],
      systemRevert: ["connect", "reconnect"],
      cacheTTSRecord: {},
      setMsgListThrottle: () => void 0,
    };
  },
  created() {
    this.listenClientAndManageEvent();
    this.throttledReadRealtimeMsg = ttsThrottled(this.readRealtimeMsg);
    this.$eventHub.$on("ai_config_change", (res) => {
      this.aiConfig = res.aiconfig;
    });
    this.$eventHub.$on("greeting_config_change", (res) => {
      this.greetingDesc = res.appGreeting;
    });
    this.$eventHub.$on("app_config_change", (res) => {
      this.robotName = res.appName;
      this.robotAvatar = res.appAvatar;
      this.greetingDesc = res.appGreeting;
    });
    // 监听公共的ws事件
    this.listenCommonEvent();
    this.setMsgListThrottle = lodash.throttle(this.setMsgList, 100);
  },
  mounted() {
    document.addEventListener("click", this.handleOutsideClick);

    const a =
      this.webIMSource === "client"
        ? window.localStorage.getItem("app_type")
        : getQueryVariable("app_type");
    this.apptype = a || APP_TYPE.QA;
    const erd = elementResizeDetectorMaker();
    const bodyDom = document.body;

    erd.listenTo(bodyDom, (element) => {
      this.chatBoxHeight = element.clientHeight - 113; // 57+56 头部的高度
    });

    const sDom = document.querySelector(".client-chat");
    // 使用防抖原因，消息变动时，会出现连续触发scroll两次，导致scroll回调两次，
    // 此时，this.jsScrolling和this.userScrolling无法和消息触动作同步，this.userScrolling判断失效，导致无法自动滚动
    // 这里预期希望是消息变动触发scroll一次或者多次，scroll回调一次
    this.clientScroller = debounce(() => {
      console.log(
        "scroll> on",
        this.jsScrolling,
        this.userScrolling,
        new Date().getTime()
      );
      this.$refs["first"].hide();
      if (
        this.msgList[this.msgList.length - 1].is_final === false &&
        !this.jsScrolling
      ) {
        this.userScrolling = true;
      } else {
        this.jsScrolling = false;
      }
    }, 100);
    sDom.addEventListener("scroll", this.clientScroller);

    // 移动端 - 页面滚动事件
    const that = this;
    this.isMobile &&
      (window.onscroll = debounce(() => {
        const scrollTop =
          document.documentElement.scrollTop || document.body.scrollTop;
        scrollTop === 0 && that.onScroll();
      }, 300));

    document.addEventListener("click", (event) => {
      if (
        window.parent &&
        window.parent.window &&
        event.target.tagName.toLowerCase() === "a" &&
        event.target.rel === "noreferrer"
      ) {
        // 拦截加了noreferrer的特殊地址，有些地址会判断referrer来拦截，比如
        // https://static.www.tencent.com/uploads/2025/04/08/1132b72b565389d1b913aea60a648d73.pdf
        event.preventDefault();
        window.parent.window.open(event.target.href, "_blank", "noreferrer");
      }
    });
  },
  beforeDestroy() {
    // 移除全局事件监听器
    document.removeEventListener("click", this.handleOutsideClick);
    const sDom = document.querySelector(".client-chat");
    sDom && sDom.removeEventListener("scroll", this.clientScroller);
    this.$eventHub.$off("upwardmsg", this.upwardmsgHandler);
    this.$eventHub.$off("reading_audio", this.readingAudioHandler);
  },
  methods: {
    getFileIcon,
    formateSize,
    fileString,
    escapeSingleTilde,
    escapeMarkdownStars,
    replaceLinks,
    formattedEllipsis,
    cb() {
      // console.log("cbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb");
    },
    getFilesList(item) {
      // console.log("getFilesList", item);
      if (
        item.agent_thought &&
        item.agent_thought.files &&
        item.agent_thought.files.length > 0
      ) {
        return item.agent_thought.files;
      } else {
        return [];
      }
    },
    changeLegend(e_charts_info) {
      // item.extra_info.e_charts_info
      try {
        if (e_charts_info && e_charts_info.length > 0) {
          for (let i = 0; i < e_charts_info.length; i++) {
            const elObj = JSON.parse(e_charts_info[i]);
            if (elObj.legend) {
              if (elObj.legend.length > 0) {
                elObj.legend.forEach((i) => {
                  i.orient = "horizontal"; // 水平布局
                  i.left = "center"; // 图例居中
                  i.top = "bottom"; // 图例在底部
                  i.padding = 5;
                  i.type = "scroll";
                });
              } else {
                elObj.legend.orient = "horizontal"; // 水平布局
                elObj.legend.left = "center"; // 图例居中
                elObj.legend.top = "bottom"; // 图例在底部
                elObj.legend.padding = 5;
                elObj.legend.type = "scroll";
              }
            }
            e_charts_info[i] = elObj;
          }
        }
        return e_charts_info;
      } catch (e) {
        console.log("图表json转换异常！");
        return e_charts_info;
      }
    },
    handleOutsideClick(event) {
      if (!this.oldPopDemo) {
        return;
      }
      const firstElement = document.getElementsByClassName("pop-demo")[0];
      console.log(
        "【clickoutside】===========>",
        event.target,
        this.oldPopDemo,
        firstElement
      );
      if (
        this.oldPopDemo.contains(event.target) ||
        firstElement.contains(event.target)
      ) {
      } else {
        if (this.oldPopDemo) {
          this.$refs["first"] &&
            this.$refs["first"].unbindTrigger(this.oldPopDemo);
        }
        // 调用你想要执行的方法
        this.$refs["first"] && this.$refs["first"].hide();
        this.oldPopDemo = null;
      }
    },
    showMultiThought(idnx, item) {
      if (item.agent_thought && item.agent_thought.reply_index) {
        return item.agent_thought.reply_index === idnx;
      } else {
        return idnx === 0;
      }
    },
    imageClick(e) {
      const img = e.target;
      console.log("img.src", img.src);
      e.preventDefault();
      window.open(img.src);
    },
    littleTagClick(e, r) {
      const findMsg = this.$clientData.getMsgById(r);
      let innerDome = e.querySelectorAll(".little-tags");
      let outerTextArr = [];
      if (innerDome && innerDome.length > 0) {
        innerDome.forEach((dom) => {
          outerTextArr.push(dom.outerText);
        });
      }
      this.popperList = findMsg.references.filter((e) =>
        outerTextArr.includes(e.id)
      );
      if (e) {
        this.$refs["first"] && this.$refs["first"].bindTrigger(e, "manual");
        this.$refs["first"] && this.$refs["first"].update();
        this.$refs["first"] && this.$refs["first"].show();
        this.oldPopDemo = e;
      }
    },
    previewDoc(item) {
      if (!item.doc_id) {
        window.open(
          `${window.location.origin}/preview?cosUrl=${encodeURIComponent(
            item.file_url
          )}&fileType=${item.file_type}&fileName=${item.file_name}`
        );
        return;
      }
      const sharecode = getShareCode();
      const botBizId =
        window.localStorage.getItem(`bot_biz_id_im_${sharecode}`) ||
        getQueryVariable("bot_biz_id");
      const tdeId = Cookies.get("_tde_id") || "";
      const cat = getUserCategory();
      tdeId
        ? window.open(
            `https://tde.xiaowei.cloud.tencent.com/preview?id=${item.doc_id}&botBizId=${botBizId}&typeKey=realtime&cat=${cat}`
          )
        : window.open(
            `https://lke.cloud.tencent.com/preview?id=${item.doc_id}&botBizId=${botBizId}&typeKey=realtime&cat=${cat}`
          );
    },
    clickCard(card, msgIndex) {
      if (!this.msgList[msgIndex].option_selected) {
        const findMsg = this.$clientData.getMsgById(
          this.msgList[msgIndex].record_id
        );
        findMsg.option_selected = true;
        this.$emit("send", card);
      }
    },
    onHeightChange(height, context) {
      const maxHeight = 277; // 设置你想要的最大高度
      context.optionExpend = height > maxHeight;
    },
    fixMarkdownOrderedList(mdText) {
      // 分割文本为行
      const lines = mdText.split("\n");
      // 遍历每一行
      for (let i = 0; i < lines.length; i++) {
        // 检查当前行是否是有序列表项
        if (/^\d+\.\s/.test(lines[i])) {
          // 检查下一行是否是错误的有序列表项
          if (lines[i + 1] && /^\d+\.\S/.test(lines[i + 1])) {
            // 在序号和点之间添加空格
            lines[i + 1] = lines[i + 1].replace(/(\d+)\.(\S)/, "$1. $2");
          }
        }
      }
      // 合并行为新的文本
      const fixedMdText = lines.join("\n");

      return this.xssHandlerProcess(fixedMdText);
    },
    fixContentWithoutImg(content) {
      const regex = /!\[.*?\]\(.*?\)/g;
      let replaced = content.replace(regex, "[正在输出图片...]");
      const contArr = replaced.split("![img](");
      if (contArr.length === 2) {
        replaced = contArr[0] + "[正在输出图片...]";
        // replaced = content.replace(contArr[1], '');
      }
      // console.log('fixContentWithoutImg', replaced);
      return this.xssHandlerProcess(replaced);
    },
    calHoverGap(index) {
      // 只在工作台才需要 hover 展示功能气泡
      if (this.webIMSource !== "workbench") return;
      if (this.msgList[index] && this.$refs[this.msgList[index].record_id]) {
        const totalWidth = document.querySelector(".qa-item").clientWidth;
        const answerWidth =
          this.$refs[this.msgList[index].record_id][0].clientWidth;
        if (totalWidth - answerWidth < 66 + 122) {
          console.log(
            "calHoverGapcalHoverGapcalHoverGapcalHoverGapcalHoverGap"
          );
          this.hoverGap = totalWidth - answerWidth - 188 + 30;
        } else {
          this.hoverGap = 5;
        }
      }
    },
    showIconHover() {
      if (!this.iconHover) {
        this.iconRender = Math.random();
        const image = new Image();
        image.src = `/SmartWebim/magichover.gif?t=${this.iconRender}`;
        image.onload = (e) => {
          this.iconHover = true;
          this.isActive = false;
        };
      }
    },
    // 发送热门问题
    sendQuestion(question) {
      this.$emit("send", question);
    },
    hideIconHover() {
      this.iconHover = false;
      this.isActive = false;
    },
    autoSearchUserAnswer(index) {
      // console.log(index, this.msgList[index]);
      this.isActive = true;
      debounceOne(() => {
        this.isActive = false;
        this.$emit("autoSearchUserAnswer", this.msgList[index]);
        console.log("autoSearchUserAnswer", index, this.msgList[index]);
      }, 300)();
    },
    // 监听用户端/管理端体验侧的ws事件
    listenClientAndManageEvent() {
      // 欢迎页发送问题，路由至会话页错过client_msgContentChange监听，需要单独渲染一次
      const welcomeQuestions = this.$clientData.getChatsContent();
      this.renderMsgList(welcomeQuestions, MESSAGE_TYPE.QUESTION);

      // 从缓存获取机器人信息
      const cachedConfig = this.$clientData.getConfigInfo();
      if (cachedConfig) {
        this.robotName = cachedConfig.name;
        this.robotAvatar = cachedConfig.avatar;
        this.aiConfig = cachedConfig.ai_call;
      }

      // 监听配置信息变更，获取机器人名称
      this.$eventHub.$on("client_configChange", (res) => {
        // console.log('233333333333avatar------>', res)
        this.greetingDesc = res.greeting;
        this.robotAvatar = res.avatar;
        this.aiConfig = res.ai_call;
        this.recommendQuestionList = res.recommend_questions || [];
        this.robotName = res.name;
        this.$eventHub.$emit("token_state_change", {
          used_count: res.token_balance,
          free_count: res.token_total,
        });
      });

      // 监听答案消息队列变更事件
      this.$eventHub.$on("client_msgContentChange", (res) => {
        const { chatsContent, type } = res;
        // console.log("触发了吗？？？");
        // PS：若新消息不属于当前机器人，则在 $clientData 中监听到ws消息后判断并屏蔽。不在此处判断和屏蔽
        this.renderMsgList(chatsContent, type);
      });

      // 监听会话转接事件
      this.$eventHub.$on("client_sessionTransfered", (res) => {
        // todo-elaine
      });

      // 监听会话结束事件
      this.$eventHub.$on("client_sessionClosed", (res) => {
        // todo-elaine
      });
    },
    // 监听公共的ws事件
    listenCommonEvent() {
      this.$eventHub.$on("data_history", () => {
        this.historyLoading = false;
      });

      this.$eventHub.$on("data_historyError", () => {
        this.historyLoading = false;
      });
      // 监听转人工变化消息
      this.$eventHub.$on("transferInfo", (res) => {
        // 当前的visitorid?
        console.log("transferInfo", res);
        this.transferStatus = res.transferStatus;
      });
      // ws链接错误
      this.$eventHub.$on("systemEvents", (args) => {
        console.log("ws error", args);
        if (
          !this.connectionError &&
          this.systemError &&
          this.systemError.indexOf(args.errorName) > -1
        ) {
          // console.log('会话连接断开，请重新生成或刷新重试');
          // this.connectionError = true;
          // this.$clientData.stopGeneration('已停止', false); // 非手动停止, 网络问题
        } else if (
          this.connectionError &&
          this.systemRevert.indexOf(args.errorName) > -1
        ) {
          this.connectionError = false;
        }
      });
    },
    upwardmsgHandler() {
      if (!this.autoPlay || !this.aiConfig.enable_voice_interact) return;
      this.$eventHub.$emit("reading_audio", {
        reading: false,
      });
      this.lastRealTimeMsgEnd = false;
      this.sendRealtimeLen = 0;
      this.$clientData.updateReadStatus({
        reading: true,
        msg: { record_id: "" },
      });
    },
    readingAudioHandler(res) {
      let audioElement = document.getElementById("audioplayer");
      // 新增全局状态控制
      let currentMediaSource = null;
      let currentObjectURL = null;
      let isLastChunk = false;
      const cleanupMediaSource = () => {
        // 停止播放时彻底释放资源
        audioElement.pause();
        audioElement.currentTime = 0;
        if (currentMediaSource) {
          if (currentMediaSource && currentMediaSource.readyState === "open") {
            Array.from(currentMediaSource.sourceBuffers).forEach((buffer) => {
              currentMediaSource.removeSourceBuffer(buffer);
            });
            currentMediaSource.endOfStream(); // 终止流
          }
          URL.revokeObjectURL(currentObjectURL);
          currentMediaSource = null;
        }
      };
      const restartAudio = (msg) => {
        const startNewAudio = () => {
          // 初始化新资源
          if (currentObjectURL) {
            // 释放老链接
            URL.revokeObjectURL(currentObjectURL);
          }
          const isMSESupported = () => {
            return window.MediaSource && MediaSource.isTypeSupported;
          };
          currentMediaSource = isMSESupported()
            ? new MediaSource()
            : new window.ManagedMediaSource();
          currentObjectURL = URL.createObjectURL(currentMediaSource);
          audioElement.src = currentObjectURL;
          connectPlay(currentMediaSource);

          // 重置播放状态
          audioElement.currentTime = 0;
          audioElement.addEventListener("playing", () => {
            console.log("媒体真正开始播放了！", performance.now());
          });
          console.log("执行播放动作", performance.now());
          audioElement.play();
          const { content, record_id, is_final } = msg;
          this.lastReadMsgId = "";
          this.markdownTTS(content, record_id, is_final, msg.isRealtime);
          // 精准判断播放结束
          currentMediaSource.addEventListener("sourceended", () => {
          });
          audioElement.addEventListener("ended", () => {
            this.$eventHub.$emit("reading_audio", {
              reading: false,
            });
            cleanupMediaSource(); // 释放资源
          });
        };
        if (currentMediaSource) {
          currentMediaSource.addEventListener("sourceclosed", startNewAudio);
          // 清理旧的SourceBuffer
          if (currentMediaSource && currentMediaSource.readyState === "open") {
            Array.from(currentMediaSource.sourceBuffers).forEach((buffer) => {
              currentMediaSource.removeSourceBuffer(buffer);
            });
            currentMediaSource.endOfStream(); // 终止流
          }
          // URL.revokeObjectURL(currentObjectURL);
        } else {
          startNewAudio();
        }
      };
      // 修改 connectPlay 函数
      function connectPlay(mediaSourceSeg) {
        let sourceBuffer;
        mediaSourceSeg.addEventListener("error", (e) => {
          mediaSourceSeg.endOfStream();
        });

        mediaSourceSeg.addEventListener("sourceopen", () => {
          try {
            sourceBuffer = mediaSourceSeg.addSourceBuffer("audio/mpeg");
            setupBufferQueue(sourceBuffer); // 传入sourceBuffer避免闭包问题
          } catch (e) {
            console.log("tts:SourceBuffer creation failed:", e);
          }
        });
      }

      // 修改缓冲区管理
      function setupBufferQueue(sourceBuffer) {
        // 接收参数
        let bufferQueue = [];
        let isUpdating = false;

        const updateHandler = () => {
          // console.log('tts:bufferQueue updateend缓冲区', isLastChunk);
          isUpdating = false;
          if (bufferQueue.length > 0 && !sourceBuffer.updating) {
            const data = bufferQueue.shift();
            sourceBuffer.appendBuffer(data);
            isUpdating = true;
          }
          if (bufferQueue.length === 0 && isLastChunk) {
            currentMediaSource.endOfStream();
          }
        };

        sourceBuffer.addEventListener("updateend", updateHandler);
        sourceBuffer.addEventListener("error", () => {

          sourceBuffer.abort();
        });
        isLastChunk = false;
      }
      // reading_audio事件参数 reading-停止(0)还是开始（0），msg-播放时的数据
      if (res.msg && res.reading) {
        restartAudio(res.msg);
      } else {
        this.sendRealtimeLen = 0;
        this.lastRealTimeMsgEnd = true;
        cleanupMediaSource();
      }
    },
    readRealtimeMsg(msg, type) {
      // if (type === MESSAGE_TYPE.ANSWER) {
      //     console.log('tts:throttledReadRealtimeMsg时消息取消前被打断', msg);
      //     return;
      // }
      if (type !== MESSAGE_TYPE.ANSWER || msg.is_from_self) {
        return;
      }
      if (this.lastRealTimeMsgEnd) {
        console.log(
          "tts:throttledReadRealtimeMsg时消息取消前被打断",
          msg,
          this.lastRealTimeMsgEnd
        );
        return;
      }
      const { content, record_id, is_final } = msg;
      const readStatus = this.$clientData.getReadStatus();
      console.log("tts:throttledReadRealtimeMsg", readStatus.record_id);
      if (readStatus.record_id !== "" && readStatus.record_id !== record_id) {
        console.log(
          "tts:throttledReadRealtimeMsg实时消息取消前被打断",
          readStatus.record_id
        );
        return;
      }
      if (content && type === MESSAGE_TYPE.ANSWER) {
        console.log(
          "tts:realtime",
          is_final,
          msg,
          this.lastRealTimeMsgEnd,
          readStatus.record_id
        );
        if (msg.is_evil) {
          return;
        }
        if (readStatus.record_id === "") {
          // 建立播放
          this.readRealtimeMsgStime = Date.now();
          msg.isRealtime = true;
          this.realTimeMsgQueue = [msg];
          this.$eventHub.$emit("reading_audio", {
            reading: true,
            msg: msg,
          });
          this.lastRealTimeMsgEnd = is_final;
        } else {
          this.lastRealTimeMsgEnd = is_final;
          this.markdownTTS(content, record_id, is_final, true);
        }
      }
    },
    processSentence(partMarkDown, record_id, isFinal) {
      // 超时结束
      // 取最后一句之前的内容
      if (isFinal) {
        return partMarkDown;
      } else {
        for (let i = partMarkDown.length - 1; i >= 0; i--) {
          const char = partMarkDown[i];
          if (SENTENCECHAR[char]) {
            return partMarkDown.substring(0, i + 1);
          }
        }
        return "";
      }
    },
    regexFilter(content) {
      const codeBlockRegex = /```[\s\S]*?```|~~~[\s\S]*?~~~/g;
      // 1. 匹配完整表格结构（含表头和分隔线）
      const fullTableRegex =
        /(\|.*\|[\r\n]+)(\|[-: ]+[-|: ]+\|[\r\n]+)(\|.*\|[\r\n]+)+/g;

      // 2. 匹配残余表格行（防止漏网）
      const tableRowRegex = /^\|.*\|$/gm;
      return (
        content
          // 第一阶段：移除代码块
          .replace(codeBlockRegex, "")
          // 第二阶段：移除完整表格
          .replace(fullTableRegex, "")
          // 第三阶段：清理残留表格行
          .replace(tableRowRegex, "")
          .replace(/`(.*?)`/g, "<code>$1</code>")
          // 3. 处理链接（兼容嵌套方括号）[3](@ref)
          .replace(/$$(.*?)$$$(.*?)$/g, (_, text, url) => {
            return `<a href="${url.trim()}">${text}</a>`;
          })
          // 4. 处理图片（兼容带空格的URL）
          .replace(/!$$(.*?)$$$(.*?)$/g, (_, alt, src) => {
            return `<img src="${src.trim()}" alt="${alt}">`;
          })
          // 5. 处理标题（兼容#号后无空格的情况）[2,4](@ref)
          .replace(/^(#{1,6})(?!#)(.*)$/gm, (_, hashes, text) => {
            const level = hashes.length;
            return `<h${level}>${text.trim()}</h${level}>`;
          })
          // 加粗（处理**text**和__text__）
          .replace(/(\*\*|__)(?=\S)((?:\\\1|.)+?)(\1)/g, "<strong>$2</strong>")

          // 斜体（处理*text*和_text_）
          .replace(/(\*|_)(?=\S)((?:\\\1|.)+?)(\1)/g, "<em>$2</em>")
          .replace(/^(\s*)[-*+] ((?:\\\]|.)+)/gm, (match, indent, content) => {
            const depth = Math.floor(indent.replace(/\S/g, "").length / 2);
            return `${"<ul>".repeat(depth)}<li>${content}</li>${"</ul>".repeat(
              depth
            )}`;
          })
          // 8. 处理段落（最后执行）
          .replace(/^((?!<h\d|<\/?ul|<\/?li|<\/?pre).+)$/gm, "<p>$1</p>")
      );
    },
    filterContent(content) {
      const env = {};
      let inTable = false;
      let inFencedCode = false;
      let inIndentCode = false;
      const tokens = md.parse(content).filter((token) => {
        // console.log('tts:parse', token.type, token);
        if (token.type === "fence") {
          inFencedCode = !inFencedCode; // 原子化状态切换
          return false; // 删除所有```标记
        }
        // 代码块闭合后立即重置状态
        if (token.type === "paragraph_open") {
          inFencedCode = false; // 显式确保状态重置
        }
        if (inFencedCode) return false;
        if (token.type === "code_block") {
          inIndentCode = true;
          return false;
        }
        if (inIndentCode && token.type !== "code_block") {
          inIndentCode = false;
        }
        if (inIndentCode) return false;
        // 表格过滤 (多层嵌套安全)
        if (token.type === "table_open") inTable = true;
        if (token.type === "table_close") {
          inTable = false;
          return false; // 删除table_close标签
        }
        if (inTable) return false;
        return true;
      });
      const html = md.renderer.render(tokens, md.options, env);
      return { html, inTable, inFencedCode };
    },
    filterEmojis(str) {
      const regex =
        /[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{1F900}-\u{1F9FF}\u{1F1E6}-\u{1F1FF}]/gu;
      return str.replace(regex, "");
    },
    markdownTTS(content, record_id, isFinal, isRealtime) {
      if (!record_id) return;
      console.log("tts:markdownTTS", this.aiConfig, isFinal);
      if (record_id !== this.lastReadMsgId) {
        this.lastReadMsgId = record_id;
      }
      if (this.realTimeMsgQueue && this.realTimeMsgQueue.length > 0) {
        const que = this.realTimeMsgQueue;
        this.realTimeMsgQueue = [];
        que.forEach((msg) => {
          if (msg.record_id !== record_id) {
            this.markdownTTS(msg.content, msg.record_id, msg.is_final, true);
          }
        });
      }
      const sentence = this.processSentence(content, record_id, isFinal);
      if (!sentence) return;
      const rmrefSentence = sentence.replace(/\[\d+(,\d+)*\]\(@ref\)/g, "");
      let rawContent = this.filterEmojis(rmrefSentence);
      let htmlContent = "";
      try {
        const filterObj = this.filterContent(rawContent); // 使用 marked.js 或 markdown-it 转换
        console.log("tts:filter markdown", filterObj);
        htmlContent = filterObj.html;
      } catch (err) {
        console.log("err:", err);
        htmlContent = this.regexFilter(rawContent);
      }

      const doc = new DOMParser().parseFromString(htmlContent, "text/html");
      const allText = doc.body.textContent.trim();
      // this.sendRealtimeLen 缓存发送长度
      if (allText) {
        const ttsContent = allText.substring(this.sendRealtimeLen);
        const timeSpan = Date.now() - this.readRealtimeMsgStime;
        if (
          isRealtime &&
          Math.floor(timeSpan / 1000) * 6 >
            this.sendRealtimeLen + ttsContent.length
        ) {
          // 没字读了就加快点读数据
          this.throttledReadRealtimeMsg.resetCount();
        }
        if (this.sendRealtimeLen === 0) {
          this.lastContent = "";
        }
        const ret = this.getRegret(
          this.lastContent,
          allText,
          this.sendRealtimeLen
        );
        const regretText = ret.s.substring(ret.index);
        if (ttsContent === "" && !isFinal) {
          return;
        }
        this.lastContent = allText;
        this.sendRealtimeLen = allText.length;
      }
    },
    getRegret(prevStr, currentStr, lastSendLen) {
      // 反悔处理，如果上一次发送的消息和本次消息一致，则把不一致的部分截取出来，发送给语音合成,保证反悔后是一个完整的句子
      let diffLen = 20;
      let prevEnd = "";
      let currentEnd = "";
      let len = 0;
      while (true) {
        prevEnd = prevStr.slice(0 - diffLen);
        len = prevEnd.length;
        currentEnd = currentStr.slice(lastSendLen - len, lastSendLen);
        if (diffLen > lastSendLen) break;
        if (prevEnd.substring(0, 3) !== currentEnd.substring(0, 3)) {
          // 基于性能考虑，只对比一个字符确定是否是前级
          diffLen = diffLen + 20;
        } else {
          break;
        }
      }

      if (prevEnd === currentEnd) return { s: currentStr, index: lastSendLen };
      let diffIndex = 0;
      const currentEndArr = currentEnd.split("");
      for (; diffIndex < len; diffIndex++) {
        if (
          prevEnd[diffIndex] !== currentEndArr[diffIndex] &&
          prevEnd[diffIndex] === "*"
        ) {
          currentEndArr.splice(diffIndex, 0, prevEnd[diffIndex]);
          continue;
        }
        if (prevEnd[diffIndex] !== currentEndArr[diffIndex]) break;
      }
      const ret = currentEndArr.join("");
      const diff = ret.substring(diffIndex);
      const pre = currentStr.slice(0, lastSendLen - len);
      console.log(
        "tts:regret",
        prevStr,
        "after:",
        currentStr,
        "ret:",
        ret,
        "diff:",
        diff,
        "index:",
        diffIndex
      );
      return {
        s: pre + ret + currentStr.substring(lastSendLen),
        index: lastSendLen - len + diffIndex,
      };
    },
    // 渲染消息会话页面
    renderMsgList(data, type) {
      // 无需滚动至底部的ws事件：用户端拉取历史记录、用户端停止生成、坐席端取历史记录、点赞点踩
      const noScrollEvt = [
        MESSAGE_TYPE.HISTORY,
        MESSAGE_TYPE.STOP,
        MESSAGE_TYPE.WORKBENCH_HISTORY,
        MESSAGE_TYPE.FEEDBACK,
      ];
      const list = data.map((el) => {
        return { ...el, showPop: true, isMdExpand: false };
      });
      // console.log(list, "renderMsgListrenderMsgList");
      const sDom1 = document.querySelector(".client-chat");
      let curScrollTop = 500;
      let oldScrollHeight = 0;
      if (sDom1) {
        oldScrollHeight = sDom1.scrollHeight;
        curScrollTop = sDom1.scrollTop;
      }
      // console.log('====================thought', this.msgList, list);
      if (list && list.length > 0) {
        // 不是自己发的，不是loading中的，不是最后一个 发新消息时候就折叠thought
        let hideList = list.filter(
          (f, idx) =>
            !f.is_from_self && !f.loading_message && idx !== list.length - 1
        );

        if (hideList && hideList.length > 0) {
          let lastItem = hideList[hideList.length - 1];
          this.$refs["thought" + lastItem.record_id] &&
            this.$refs["thought" + lastItem.record_id][0] &&
            this.$refs["thought" + lastItem.record_id][0].collapseOperate(
              false
            );
        }
      }
      // console.log('=======更新消list========', clone(list));
      const cloneMsgList = clone(
        list.map((l) => {
          // https://tapd.woa.com/tapd_fe/70080800/story/detail/1070080800121595559
          const optionCardIndex = _get(
            l,
            "tokens_msg.procedures.0.debugging.work_flow.option_card_index"
          );
          if (optionCardIndex && optionCardIndex.record_id) {
            const { index, record_id } = optionCardIndex;
            const tempIndex = list.find((m) => m.record_id === record_id);
            console.log("optionCardIndex111111", tempIndex, index, record_id);

            if (tempIndex) {
              tempIndex.option_card_index = index - 1;
            }
          }
          return l;
        })
      );
      this.setMsgListThrottle(cloneMsgList);
      if (
        this.autoPlay &&
        cloneMsgList.length > 0 &&
        window.$initTypeKey !== 4
      ) {
        const lastMsg = cloneMsgList[cloneMsgList.length - 1];
        if (!lastMsg.is_history) {
          this.throttledReadRealtimeMsg(lastMsg, type);
        }
      }
      // 对话框滚动至底部（部分ws事件类型无需执行滚动）
      this.$nextTick(() => {
        const sDom = document.querySelector(".client-chat");

        if (!sDom) return;

        if (
          type === MESSAGE_TYPE.HISTORY ||
          (type === MESSAGE_TYPE.WORKBENCH_HISTORY && !this.isFirstGetHistory)
        ) {
          const tempTop = sDom.scrollHeight - oldScrollHeight + curScrollTop;
          this.jsScrolling = true;
          scrollToBottom(sDom, tempTop);
        }
        if (
          !this.userScrolling &&
          (!noScrollEvt.includes(type) ||
            (this.webIMSource === "workbench" && this.isFirstGetHistory))
        ) {
          this.jsScrolling = true;
          scrollToBottom(sDom, sDom.scrollHeight);
        }
        if (
          this.msgList.length > 0 &&
          this.msgList[this.msgList.length - 1].is_final === true
        ) {
          this.userScrolling = false;
        }
      });
    },
    setMsgList(msg) {
      this.msgList = msg;
    },
    getHistoryList() {
      this.historyLoading = true;
      this.isFirstGetHistory = false;

      if (this.webIMSource === "workbench") {
        this.$eventHub.$emit("getHistoryMsg");
      } else {
        this.$clientData.getHistory(true);
      }
    },
    // 对话框上下滚动事件
    onScroll() {
      if (this.historyLoading) {
        return;
      }
      // if (this.webIMSource === 'manage') return; // 管理后台不支持滑动拉取历史记录
      this.getHistoryList();
    },
    // 收缩答案md
    onExpandMd(item) {
      // this.isMdExpand = !this.isMdExpand;
      console.log("isMdExpandisMdExpand=====>", item);
      item.isMdExpand = !item.isMdExpand;
    },
    // 打开搜索引起检索到的参考来源地址
    openSearchUrl(refer, index) {
      console.log("【reference】", refer, index);
      window.open(refer.url);
    },
    // 接收thought-collapse 组件的openBottomDrawer
    openBottomDrawer(isOpen, refer) {
      if (!refer) {
        this.$emit("toggleworkspace", false);
      }
    },
    // 跳转参考来源页
    openSourceDrawer(refers, index, event) {
      if (event) {
        event.preventDefault();
      }
      if (this.webIMSource === "manage" || this.isExperience) {
        this.$emit("closeworkspace", true);
        const payload = JSON.stringify({
          from: "webim",
          type: "audit-reference",
          record: refers,
          openId: refers[index].ids[0],
          openIndex: index,
        });
        window.parent.postMessage(payload, "*");
      } else {
        // const url = refers[index].url;
        // url && window.open(url);
        // console.log(2333333333, refers, index);
        this.$emit("openBottomDrawer", true, refers[index]);
      }
    },
    // 点击澄清问题
    handleClarifyQuestion(e, question) {
      if (!this.msgList[this.msgList.length - 1].is_final) {
        this.$message.warning("正在回答中，请稍后再试");
        return;
      }
      e.preventDefault();
      this.$emit("send", question);
    },
    onOpenWorkspace(msgIndex, procedureIndex) {
      if (this.msgList[msgIndex].is_final) {
        this.$emit("openWorkspace", this.msgList[msgIndex], procedureIndex);
      }
    },
  },
};
</script>

<style lang="less" scoped>
#audioplayer {
  display: none;
}
// .download-card-container {
//     background-color: pink;
//     display: flex;
//     flex-wrap: wrap;
//     width: 100%; /* 占满父容器宽度 */
//     box-sizing: border-box;
//     gap: 8px;
.download-card {
  max-width: calc((100% - 16px) / 3);
  background-color: aqua;
  flex: 0 0 calc((100% - 16px) / 3); /* 2个gap=16px，平均分配 */
  box-sizing: border-box;
  height: 60px;
  padding: 10px;
  // margin-bottom: 10px;
  // text-align: center;

  border-radius: var(--bubble---c-bubble-radius, 6px);
  border: 1px solid var(--color-border-normal);
  background: var(--color-fill-normal);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;

  .download-card-content {
    flex: 1;
    overflow: hidden;
  }
  .name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    // font-family: "PingFang SC";
    font-size: 14px;
    font-style: normal;
    font-weight: 400;
    line-height: 20px; /* 142.857% */
    color: var(--text---c-color-text-primary, rgba(0, 1, 12, 0.93));
  }
  .size {
    // /* caption/--caption-regular */
    // font-family: "PingFang SC";
    font-size: 12px;
    font-style: normal;
    font-weight: 400;
    line-height: 16px; /* 133.333% */
    color: var(--text---c-color-text-caption, rgba(0, 11, 54, 0.41));
  }
}
// }
// .answer-info {
//     /deep/ .little-tags{
//         word-break: normal;
//         word-wrap: normal;
//     }
// }
.ept-content {
  color: #979797;
  font-family: "PingFang SC";
  font-size: 14px;
  font-style: normal;
  font-weight: 400;
  line-height: 20px;
}
/deep/ .thought-clps-content .thought-detail.last-item {
  border-bottom: none !important;
}
/deep/ .answer-md > ol,
/deep/ .answer-md > ul {
  padding-left: 1.5em;
}

/deep/ .answer-md ol ol,
/deep/ .answer-md ul ul,
/deep/ .answer-md ul ol,
/deep/ .answer-md ol ul {
  padding-left: 1.2em;
}
/deep/ .answer-md ::marker {
  color: #000;
}
/deep/ .answer-md hr {
  border: none;
  height: 0.5px;
  background: var(--color-divider-normal);
}
</style>

<style lang="less">
.client-chat::-webkit-scrollbar {
  display: none;
}
.pop-demo {
  // background-color: pink;
  padding: 10px;
  display: flex;
  min-width: var(--size-l);
  padding: var(--spacing-base);
  flex-direction: column;
  justify-content: center;
  // align-items: center;
  gap: var(--spacing-tight);

  border-radius: var(--radius-normal);
  border: 0.5px solid var(--color-border-normal);

  background: var(--color-bg-2);
  /* shadow/--shadow-medium */
  box-shadow: var(--shadow-medium-x-1) var(--shadow-medium-y-2)
      var(--shadow-medium-blur-1) var(--shadow-medium-spread-1)
      var(--shadow-medium-color-1),
    var(--shadow-medium-x-2) var(--shadow-medium-y-2)
      var(--shadow-medium-blur-2) var(--shadow-medium-spread-2)
      var(--shadow-medium-color-2),
    var(--shadow-medium-x-3) var(--shadow-medium-y-3)
      var(--shadow-medium-blur-3) var(--shadow-medium-spread-3)
      var(--shadow-medium-color-3);

  .v-popper__arrow {
    display: block;
  }
  .pop-demo-list {
    color: var(--color-link-normal);
    /* caption/--caption-regular */
    font-family: var(--font-family-normal);
    font-size: 12px;
    font-style: normal;
    font-weight: 400;
    line-height: 16px; /* 133.333% */
    .v-button {
      text-decoration: none;
      text-align: left;
    }
  }
}
.chat-pop {
  border-radius: 6px;
  padding: 3px;
  margin-top: 5px;
  .v-popper__arrow {
    display: none;
  }
  .answer-func-icon {
    display: flex;
    align-items: center;
    height: 30px;
    .icon-divider {
      width: 1px;
      height: 14px;
      background: rgba(18, 42, 79, 0.08);
      margin: 9px 4px;
    }
    .v-icon--fill {
      fill: rgba(1, 11, 50, 0.41);
    }
    .right-icon-div {
      padding: 4px;
      width: 22px;
      height: 22px;
      border-radius: 3px;
      text-align: center;
      &:hover {
        background-color: rgba(36, 56, 97, 0.05);
        border-radius: 3px;
      }
    }
    .right-icon {
      cursor: pointer;
      margin-top: 1px;
    }
    .active-icon.v-icon--fill {
      fill: #293eb6;
    }
    .magic-hover {
      width: 20px;
      height: 20px;
    }
  }
}
.tag-loading {
  // margin: 1em 0;
  // width: 90px;
  width: fit-content;
  &:after {
    content: ".";
    animation: ellipsis 1.5s steps(1, end) infinite;
  }
}

.client-chat {
  video {
    width: 400px;
    // max-width: 295px;
    // max-width: 400px;
    // width: 95%;
  }
  display: flex;
  flex-direction: column;
  flex: 1;
  // overlay 属性在firefox下无效
  overflow-y: overlay; /* 尝试使用 overlay */
  overflow-y: scroll; /* 如果 overlay 不支持，则使用 scroll */
  padding: 0 var(--spacing-quite-loose);

  .loading {
    margin: 1em 0;
    // width: 90px;
    width: fit-content;
    display: flex;
    align-items: center;
    &::after {
      content: ".";
      animation: ellipsis 1.5s steps(1, end) infinite;
    }
    &::before {
      content: "";
      display: inline-block;
      height: 22px;
      width: 22px;
      margin-right: 8px;
      // background: url('../../../assets/img/loading.gif') no-repeat;
      background-size: contain;
    }
  }

  @keyframes ellipsis {
    0% {
      content: ".";
    }
    33% {
      content: "..";
    }
    66% {
      content: "...";
    }
    100% {
      content: ".";
    }
  }

  .transfer-info {
    width: 100%;
    margin: auto;
    font-weight: 400;
    font-size: 14px;
    color: var(--color-text-primary);
    text-align: center;
    .chat-status-item {
      max-width: 90%;
      padding: 4px 16px;
      background: #f4f5f7;
      //   width: 150px;
      opacity: 0.8;
      margin: 6px auto;
      border-radius: 6px;
      .status-text {
        width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        display: inline-block;
        font-weight: 400;
        font-size: 12px;
        color: var(--color-text-caption);
      }
    }
  }

  .qa-history {
    text-align: center;

    &.isMobile {
      margin-top: 65px;
    }

    .v-spinner__tip,
    .v-spinner__container__default {
      font-size: 12px;
      color: var(--color-text-primary);
    }
  }

  .break-releation-class {
    margin: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .table-style {
    display: inline-block;
    white-space: nowrap;
    max-width: 100%;
    overflow: scroll;
    background: white;
    border-bottom: 1px solid rgba(18, 42, 79, 0.08);
    border-right: 1px solid rgba(18, 42, 79, 0.08);
    border-spacing: 0;
    border-collapse: collapse;
    th {
      color: rgba(1, 11, 50, 0.41);
      padding: 12px;
      font-weight: 400;
      background: #eaecef;
    }
    td,
    th {
      border-left: 1px solid rgba(18, 42, 79, 0.08);
      border-top: 1px solid rgba(18, 42, 79, 0.08);
    }
    td {
      padding: 8px 4px;
      min-width: 45px;
      overflow-wrap: break-word;
      white-space: break-spaces;
    }
  }
  .qa-item {
    word-break: break-all;
    display: flex;
    flex-direction: column;
    margin-bottom: 16px;
    color: var(--color-text-primary);
    font-size: 15px;
    font-style: normal;
    font-weight: 400;
    line-height: 22px;

    .timestamp {
      font-weight: 400;
      font-size: 12px;
      line-height: 16px;
      text-align: center;
      color: var(--color-text-caption);
      margin: 16px 0;
    }

    p {
      //   margin: 12px 0;
      font-size: 16px;
      margin: 0;
      line-height: 22px;
    }

    .question-item {
      display: flex;
      align-items: center;
      width: fit-content;
      text-align: center;
      align-self: flex-end;
      padding-left: 44px;
      margin-bottom: 10px;

      &:last-child {
        margin-bottom: 0px;
      }

      &.question-doc-item {
        cursor: pointer;
        display: flex;
        width: 200px;
        padding: var(--spacing-base);
        align-items: center;
        gap: var(--spacing-tight);
        border-radius: var(--radius-large);
        background: var(--color-bg-1);
        /* shadow/--shadow-small-light */
        box-shadow: 0px 0px 9px rgba(18, 19, 25, 0.1);
        position: relative;
        .doc-icon-cont {
          display: flex;
          width: 36px;
          height: 36px;
          padding: 3px 4.5px;
          justify-content: center;
          align-items: center;
          flex-shrink: 0;
          border-radius: 1.5px;
          .doc-icon {
            width: 27px;
            height: 30px;
            flex-shrink: 0;
          }
        }
        .doc-txt-cont {
          text-align: left;
          .doc-txt-info {
            color: var(--color-text-caption);

            /* caption/--caption-regular */
            font-family: "PingFang SC";
            font-size: 12px;
            font-style: normal;
            font-weight: 400;
            line-height: 16px; /* 133.333% */
          }
          .filename {
            text-overflow: ellipsis;
            white-space: nowrap;
            overflow: hidden;
            color: var(--color-text-primary);
            width: 138px;
            /* body/--body-regular */
            font-family: "PingFang SC";
            font-size: 14px;
            font-style: normal;
            font-weight: 400;
            line-height: 20px; /* 142.857% */
          }
        }
      }
      &.question-image-item {
        align-items: flex-end;
        justify-content: flex-end;
        padding-bottom: 16px;
        gap: 12px;
        .qs-error,
        .qs-loading {
          margin-bottom: 15px;
          margin-right: 0;
        }
        p {
          margin: 0;
          line-height: 0;
        }
      }

      .qs-error {
        min-width: 16px;
        margin-right: 10px;
        color: var(--color-error-normal);
      }
      .qs-loading {
        margin-right: 10px;
      }
      .question-text {
        p {
          margin: 12px 0;
        }
      }
      .question-text,
      .question-image {
        background: #dbe8ff; // var(--bubble-bg-myself-normal);
        border-radius: 6px;
        padding: 0 16px;
        text-align: left;
        word-break: break-all;
        word-wrap: break-word;
      }
      .question-text.question-text-mobile {
        code {
          white-space: break-spaces;
        }
        img {
          max-width: 80%;
          max-height: 320px;
          border-radius: 6px;
        }
      }

      .question-text {
        code {
          white-space: break-spaces;
        }
        img {
          // max-width: 80%;
          max-width: 480px;
          max-height: 320px;
          border-radius: 6px;
        }
      }
      .question-image {
        background: none;
        padding: 0;
        img {
          border-radius: 6px;
          border: 0.5px solid var(--border---color-border-normal);
          max-width: 100%;
          max-height: 320px;
        }
      }
    }

    .transfer-item {
      margin-bottom: 16px;
      display: flex;
      flex-direction: column;
      align-items: center;
      align-self: center;
      padding: 28px 24px 12px;
      background: var(--color-bg-1);
      box-shadow: 0px 8px 24px rgba(18, 19, 25, 0.08),
        0px 0px 12px rgba(18, 19, 25, 0.04), 0px 0px 1px rgba(18, 19, 25, 0.08);
      border-radius: 6px;
      font-weight: 400;
      font-size: 12px;
      margin-top: 50px;

      .transfer-avatar {
        margin-top: -50px;
        width: 40px;
        height: 40px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
      }
      .transfer-text {
        margin-top: 8px;
        color: var(--color-text-secondary);
      }
      .transfer-timestamp {
        margin-top: 4px;
        color: var(--color-text-caption);
      }
    }

    .chat-status-item {
      max-width: 90%;
      align-self: center;
      margin: 16px 0;
      padding: 4px 16px;
      background: #f4f5f7;
      opacity: 0.8;
      border-radius: 6px;
      display: inline-flex;
      justify-content: space-between;
      align-items: center;

      .status-icon {
        display: inline-block;
        width: 8px;
        height: 8px;
        border-radius: 8px;
        margin-right: 5px;
      }
      .status-icon.gray {
        background-color: var(--color-gray-6);
      }
      .status-icon.green {
        background-color: var(--color-success-normal);
      }
      .status-icon.red {
        background-color: var(--color-error-normal);
      }
      .status-text {
        width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        display: inline-block;
        font-weight: 400;
        font-size: 12px;
        // width: 97%;
        color: var(--color-text-caption);
      }
    }

    .summary-item {
      align-self: center;
      margin: 12px 0;
    }

    .answer-item {
      display: flex;

      .contacter-avatar {
        width: 32px;
        height: 32px;
        border-radius: 50%;
        margin-right: 12px;
        display: flex;
        align-items: center;
        justify-content: center;
      }

      .summary-info {
        position: relative;
        display: flex;
        flex-direction: column;
        padding: 0 16px;
        background: #f4f5f7; // var(--bubble-bg-each-other-normal);
        border-radius: 6px;
        width: fit-content;
      }
      .classify-info {
        position: relative;
        padding: 12px 12px;
        background: #f4f5f7; // var(--bubble-bg-each-other-normal);
        border-radius: 6px;
        width: fit-content;
        display: flex;
        flex-direction: column;
        gap: 4px;
        .classify-tag {
          display: flex;
          padding: var(--spacing-tight) var(--spacing-base);
          justify-content: center;
          align-items: center;
          border-radius: var(--radius-normal);
          border: var(--border-normal) solid var(--color-brand-1);
          background: var(--color-brand-0);
          color: var(--color-brand-5);
          font-family: "PingFang SC";
          font-size: 12px;
          font-style: normal;
          font-weight: 400;
          line-height: 16px;
        }
        .classify-container {
          display: flex;
          align-content: center;
          word-break: break-all;
          .classify-label {
            height: 26px;
            line-height: 26px;
          }
          .classify-content {
            display: flex;
            flex-wrap: wrap;
            flex: 1;
            gap: 4px;
          }
        }
      }
      .answer-avatar {
        // background-color: #293EB6;
      }

      .answer-info-container {
        // background: pink;
        // width: 100%
        width: calc(100% - 55px);
      }

      .answer-info {
        position: relative;
        display: flex;
        flex-direction: column;
        // padding: 0 16px;
        padding: 0 0;
        // background: #F4F5F7; // var(--bubble-bg-each-other-normal);
        // border-radius: 6px;
        // width: fit-content;
        width: 100%;

        .answer-options {
          line-height: 40px;
          max-width: 310px;
          overflow-y: scroll;
          padding-bottom: 6px;
          &::-webkit-scrollbar {
            background: transparent;
            width: 5px;
            height: 8px;
          }
          /* 滚动条轨道 */
          &::-webkit-scrollbar-track {
            background: transparent;
            width: 0px;
          }

          /* 滚动条滑块 */
          &::-webkit-scrollbar-thumb {
            border-radius: 10px;
            background: rgba(17, 32, 70, 0.13);
          }
          .option-card {
            cursor: pointer;
            margin-right: 8px;
            padding: 6px 12px;
            display: inline-block;
            border-radius: 15px;
            border: var(--border-thickness) solid #4a70ff;
            background: #fff;
            color: #4a70ff;
            text-align: center;
            font-family: PingFang SC;
            font-size: 13px;
            font-style: normal;
            font-weight: 400;
            line-height: normal;
            max-width: 90%;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            &.disabled {
              border-radius: 15px;
              border: var(--border-thickness) solid var(--color-primary-disable);
              background: #fff;
              color: var(--color-primary-disable);
              text-align: center;
              font-family: "PingFang SC";
              font-size: 13px;
              font-style: normal;
              font-weight: 400;
              line-height: normal;
              pointer-events: none;
            }
            &.is-active {
              border-color: var(--color-primary-normal);
              color: var(--color-primary-normal);
              pointer-events: none;
            }
          }
        }

        .answer-expand {
          display: flex;
          justify-content: center;
          align-items: center;
          cursor: pointer;
          width: 44px;
          height: 24px;
          margin-bottom: 12px;
          background: var(--color-bg-2);
          box-shadow: var(--shadow-small-light);
          border-radius: 16px;
          align-self: center;
        }
        .stop-ws {
          color: var(--color-text-caption);
          margin-left: 5px;
        }
        .answer-source {
          margin: 12px 0;
          font-size: 14px;
          color: var(--color-text-caption);
          text-align: left;

          .v-button {
            text-decoration: none;
            text-align: left;
          }
        }
      }

      .answer-clarify {
        > div {
          max-width: 500px;
          display: flex;
          padding: var(--spacing-base) var(--spacing-base-loose);
          flex-direction: column;
          justify-content: center;

          border-radius: var(--radius-large);
          border: var(--border-thickness) solid var(--color-border-normal);
          background: var(--inverse-opacity-4);

          /* shadow/--shadow-small-light */
          box-shadow: var(--shadow-small-light-x-1)
              var(--shadow-small-light-y-1) var(--shadow-large-light-blur-1)
              var(--shadow-small-light-spread-1)
              var(--shadow-small-light-color-1),
            var(--shadow-small-light-x-2) var(--shadow-small-light-y-2)
              var(--shadow-small-light-blur-2)
              var(--shadow-small-light-spread-2)
              var(--shadow-small-light-color-2),
            var(--shadow-small-light-x-3) var(--shadow-small-light-y-3)
              var(--shadow-small-light-blur-3)
              var(--shadow-small-light-spread-3)
              var(--shadow-small-light-color-3);

          color: var(--color-primary-normal);

          /* caption/--caption-medium */
          font-family: "PingFang SC";
          font-size: 12px;
          font-style: normal;
          font-weight: 500;
          line-height: 16px; /* 133.333% */
          cursor: pointer;
          margin-bottom: 12px;
        }
      }
    }
  }
  // .qa-item:last-child {
  //   padding-bottom: 120px;
  // }
  :root {
    --self-padding-bottom: 120px;
  }
  .qa-item-last {
    // padding-bottom: var(--self-padding-bottom);
    padding-bottom: 120px;
    // background: peachpuff;
  }
  .qa-null {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 80%;
    text-align: center;

    .main-desc {
      font-weight: 600;
      font-size: 20px;
      color: var(--color-text-primary);
      margin: 20px;
    }
    .sub-desc {
      font-weight: 400;
      font-size: 14px;
      color: var(--color-text-caption);
    }
    .title-desc {
      // margin-top: 10px;
      color: var(--color-text-primary);
      margin-top: 16px;
      // color: #000;
      // font-family: "PingFang SC";
      font-size: 20px;
      font-style: normal;
      font-weight: 600;
      line-height: 28px; /* 140% */
    }
    .title-robot-avatar {
      border-radius: var(--radius-super-large);
      width: 64px;
      height: 64px;
      overflow: hidden;
      vertical-align: middle;
      flex-shrink: 0;
      img {
        width: 64px;
        height: 64px;
        border-radius: var(--radius-super-large);
        box-sizing: border-box;
      }
    }
    .greet-desc {
      font-size: 14px;
      line-height: 22px;
      color: var(--color-text-secondary);
      max-width: 860px;
      word-break: break-all;
      text-align: start;
      margin-top: 24px;
      display: flex;
      padding: 10px 12px;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      border-radius: 6px;
      border: 0.5px solid var(--color-border-normal);
      background: var(--inverse-opacity-4);
      /* shadow/--shadow-small-light */
      box-shadow: var(--shadow-small-light-x-1) var(--shadow-small-light-y-1)
          var(--shadow-large-light-blur-1) var(--shadow-small-light-spread-1)
          var(--shadow-small-light-color-1),
        var(--shadow-small-light-x-2) var(--shadow-small-light-y-2)
          var(--shadow-small-light-blur-2) var(--shadow-small-light-spread-2)
          var(--shadow-small-light-color-2),
        var(--shadow-small-light-x-3) var(--shadow-small-light-y-3)
          var(--shadow-small-light-blur-3) var(--shadow-small-light-spread-3)
          var(--shadow-small-light-color-3);
    }
  }
}

.client-chat.mobile-chat {
  video {
    max-width: 295px;
    width: 95%;
  }
}
.tokens-board-class {
  // margin: 8px 0 0 44px
  margin: 8px 0 0 0;
}

.answer-feedback {
}
</style>